// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   MyBoxWidget
 * @brief   orthogonal hexahedron 3D widget
 *
 * This 3D widget defines a region of interest that is represented by an
 * arbitrarily oriented hexahedron with interior face angles of 90 degrees
 * (orthogonal faces). The object creates 7 handles that can be moused on and
 * manipulated. The first six correspond to the six faces, the seventh is in
 * the center of the hexahedron. In addition, a bounding box outline is shown,
 * the "faces" of which can be selected for object rotation or scaling. A
 * nice feature of the object is that the MyBoxWidget, like any 3D widget,
 * will work with the current interactor style. That is, if MyBoxWidget does
 * not handle an event, then all other registered observers (including the
 * interactor style) have an opportunity to process the event. Otherwise, the
 * MyBoxWidget will terminate the processing of the event that it handles.
 *
 * To use this object, just invoke SetInteractor() with the argument of the
 * method a vtkRenderWindowInteractor.  You may also wish to invoke
 * "PlaceWidget()" to initially position the widget. The interactor will act
 * normally until the "i" key (for "interactor") is pressed, at which point the
 * MyBoxWidget will appear. (See superclass documentation for information
 * about changing this behavior.) By grabbing the six face handles (use the
 * left mouse button), faces can be moved. By grabbing the center handle
 * (with the left mouse button), the entire hexahedron can be
 * translated. (Translation can also be employed by using the
 * "shift-left-mouse-button" combination inside of the widget.) Scaling is
 * achieved by using the right mouse button "up" the render window (makes the
 * widget bigger) or "down" the render window (makes the widget smaller). To
 * rotate MyBoxWidget, pick a face (but not a face handle) and move the left
 * mouse. (Note: the mouse button must be held down during manipulation.)
 * Events that occur outside of the widget (i.e., no part of the widget is
 * picked) are propagated to any other registered obsevers (such as the
 * interaction style).  Turn off the widget by pressing the "i" key again.
 * (See the superclass documentation on key press activiation.)
 *
 * The MyBoxWidget is very flexible. It can be used to select, cut, clip, or
 * perform any other operation that depends on an implicit function (use the
 * GetPlanes() method); or it can be used to transform objects using a linear
 * transformation (use the GetTransform() method). Typical usage of the
 * widget is to make use of the StartInteractionEvent, InteractionEvent, and
 * EndInteractionEvent events. The InteractionEvent is called on mouse
 * motion; the other two events are called on button down and button up
 * (either left or right button).
 *
 * Some additional features of this class include the ability to control the
 * rendered properties of the widget. You can set the properties of the
 * selected and unselected representations of the parts of the widget. For
 * example, you can set the property for the handles, faces, and outline in
 * their normal and selected states.
 *
 * The box widget can be oriented by specifying a transformation matrix.
 * This transformation is applied to the initial bounding box as defined by
 * the PlaceWidget() method. DO NOT ASSUME that the transformation is applied
 * to a unit box centered at the origin; this is wrong!
 *
 * @sa
 * vtk3DWidget vtkPointWidget vtkLineWidget vtkPlaneWidget
 * vtkImplicitPlaneWidget vtkImagePlaneWidget
 */

#ifndef VTKTRANSFORMWIDGET_H
#define VTKTRANSFORMWIDGET_H

#include "vtk3DWidget.h"
#include <array>

VTK_ABI_NAMESPACE_BEGIN
    class vtkActor;
class vtkCellPicker;
class vtkPlanes;
class vtkPoints;
class vtkPolyData;
class vtkPolyDataMapper;
class vtkProp;
class vtkProperty;
class vtkSphereSource;
class vtkTransform;
class vtkConeSource;
class vtkAssembly;
class vtkArrowSource;
class vtkRegularPolygonSource;
class vtkCursor3D;
class vtkMatrix4x4;

struct Arrow_t{
    vtkArrowSource* Source;
    vtkPolyDataMapper* Mapper;
    vtkActor* Actor;

    Arrow_t();
    ~Arrow_t();
};

struct Circle_t{
    vtkRegularPolygonSource* Source;
    vtkPolyDataMapper* Mapper;
    vtkActor* Actor;

    Circle_t();
    ~Circle_t();
};
class MyBoxWidget : public vtk3DWidget
{
public:
    /**
   * Instantiate the object.
   */
    static MyBoxWidget* New();

    vtkTypeMacro(MyBoxWidget, vtk3DWidget);
    void PrintSelf(ostream& os, vtkIndent indent) override;

    ///@{
    /**
   * Methods that satisfy the superclass' API.
   */
    void SetEnabled(int) override;
    void PlaceWidget(double bds[6]) override;
    void PlaceWidget() override { this->Superclass::PlaceWidget(); }
    void PlaceWidget(
        double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) override
    {
        this->Superclass::PlaceWidget(xmin, xmax, ymin, ymax, zmin, zmax);
    }
    ///@}

    /**
   * Retrieve a linear transform characterizing the transformation of the
   * box. Note that the transformation is relative to where PlaceWidget
   * was initially called. This method modifies the transform provided. The
   * transform can be used to control the position of vtkProp3D's, as well as
   * other transformation operations (e.g., vtkTransformPolyData).
   */
    virtual void GetTransform(vtkTransform* t);
    virtual void GetTransformOfRotate(vtkTransform* t);

    /**
   * Set the position, scale and orientation of the box widget using the
   * transform specified. Note that the transformation is relative to
   * where PlaceWidget was initially called (i.e., the original bounding
   * box).
   */
    virtual void SetTransform(vtkTransform* t);

    ///@{
    /**
   * Get the handle properties (the little balls are the handles). The
   * properties of the handles when selected and normal can be
   * set.
   */
    vtkGetObjectMacro(TranslateProperty, vtkProperty);
    vtkGetObjectMacro(RotationProperty, vtkProperty);
    vtkGetObjectMacro(CenterProperty, vtkProperty);
    ///@}

    ///@{
    /**
   * Switches handles (the spheres) on or off by manipulating the actor
   * visibility.
   */
    void TranslateHandlesOn();
    void TranslateHandlesOff();

    void RotationHandlesOn();
    void RotationHandlesOff();
    ///@}

    ///@{
    /**
   * Control the behavior of the widget. Translation, rotation, and
   * scaling can all be enabled and disabled.
   */
    vtkSetMacro(TranslationEnabled, vtkTypeBool);
    vtkGetMacro(TranslationEnabled, vtkTypeBool);
    vtkBooleanMacro(TranslationEnabled, vtkTypeBool);
    vtkSetMacro(ScalingEnabled, vtkTypeBool);
    vtkGetMacro(ScalingEnabled, vtkTypeBool);
    vtkBooleanMacro(ScalingEnabled, vtkTypeBool);
    vtkSetMacro(RotationEnabled, vtkTypeBool);
    vtkGetMacro(RotationEnabled, vtkTypeBool);
    vtkBooleanMacro(RotationEnabled, vtkTypeBool);
    ///@}

protected:
    MyBoxWidget();
    ~MyBoxWidget() override;

    // Manage the state of the widget
    int State;
    enum WidgetState : int
    {
        Start = 0,
        Moving,
        Scaling,
        Rotation,
        Outside
    };

    int CurHandle;
    enum SelectHandle : int
    {
        XTranslate = 0,
        YTranslate,
        ZTranslate,
        XRotation,
        YRotation,
        ZRotation,
        Cursor,
        NoneSelect
    };

    // Handles the events
    static void ProcessEvents(
        vtkObject* object, unsigned long event, void* clientdata, void* calldata);

    // ProcessEvents() dispatches to these methods.
    virtual void OnMouseMove();
    virtual void OnLeftButtonDown();
    virtual void OnLeftButtonUp();

    //组合
    vtkAssembly* Assembly;
    vtkTransform* Transform;
    vtkTransform* RotateTransform;
    vtkMatrix4x4* OriginMatrix;

    // the cursor3D
    vtkActor* Actor;
    vtkPolyDataMapper* Mapper;
    vtkCursor3D* Cursor3D;

    //移动箭头
    std::array<Arrow_t,3> Arrows;

    //旋转圆圈
    std::array<Circle_t,3> Circles;

    // glyphs representing hot spots (e.g., handles)
    // virtual void PositionHandles();

    // Do the picking
    vtkCellPicker* TranlatePicker;
    vtkCellPicker* RotatePicker;

    // Register internal Pickers within PickingManager
    void RegisterPickers() override;

    // Methods to manipulate the hexahedron.
    virtual void Translate(double*);
    virtual void Rotate(int X, int Y, double* p1, double* p2, double* vpn,double*);

    // Properties used to control the appearance of selected objects and
    // the manipulator in general.
    vtkProperty* TranslateProperty;
    vtkProperty* RotationProperty;
    vtkProperty* CenterProperty;
    void CreateDefaultProperties();

    // Control whether scaling, rotation, and translation are supported
    vtkTypeBool TranslationEnabled;
    vtkTypeBool ScalingEnabled;
    vtkTypeBool RotationEnabled;

private:
    MyBoxWidget(const MyBoxWidget&) = delete;
    void operator=(const MyBoxWidget&) = delete;
};

VTK_ABI_NAMESPACE_END
#endif // VTKTRANSFORMWIDGET_H
